/*
 * @Author: your name
 * @Date: 2021-06-14 12:23:19
 * @LastEditTime: 2021-10-20 06:46:44
 * @LastEditors: Please set LastEditors
 * @Description: 二级页管理类，如果一个页面需要对设置项进行选定操作，可继承此类进行管理
 * 注意：该类反向依赖派生类，由派生类指定模板数据类型
 */
#ifndef SECOND_HANDLE_H
#define SECOND_HANDLE_H
#include "second_page.h"
#include "page_handle.h"
#include "interface.h"
#include "item.h"
#include <mutex>
/**
 * @brief 
 * 虚拟功能，当界面设置项个数随机时使用
 */
enum class VirtualFunc
{
    FUNCTION_START = 0,
    FUNCTION_END = 255
};
/**
 * @brief 
 * 设置项二级页管理类
 * @tparam FUNC 派生类的功能枚举
 * @tparam PAGE 派生类的界面类
 * @tparam KEYEVENT 派生类的按键功能映射数据
 */
template <typename FUNC, typename PAGE, typename KEYEVENT>
class SecondHandle
{
public:
    SecondHandle();
    /**
     * @brief Construct a new Second Handle object
     * 本类被继承时，必须由派生类初始化的变量
     * @param itemTotal 设置项单页显示的最大的设置项数
     * @param page 派生类的窗口控件指针
     */
    SecondHandle(const unsigned int itemTotal, std::unique_ptr<Widget> &page)
        : primaryWidget(page), ITEM_TOTAL(itemTotal)
    {
        selectLoop = true;
    }
    virtual ~SecondHandle() = default;
    void SecondPageReset(SecondPage *value);
    SecondPage &GetSecondPage();
    bool SecondPageValid();
    bool SwitchSelectedItemHandle(Widget *window, int funcSelected, int lastSelected);
    // bool CreateItemHandle(KeyboardValue key, Widget *window, int funcSelected, Widget *view2);
    bool CreateItemHandleV2(unsigned int itemNum, Widget *window, int funcSelected, Widget *view2);
    bool CreateItemHandleV3(unsigned int itemNum, Widget *window, int funcSelected, Widget *view2);
    void DestroyHandle(Widget *window, int itemNum);
    void StartDisplayHandle(Widget *window, int funcSelected, std::function<void(Widget *, int)> createOne, unsigned int maxItem = 4);
    void StartDisplayHandleV2(Widget *window, int funcSelected, std::function<void(Widget *, int)> createOne, unsigned int maxItem = 6);
    void RefreshItemHandle(void *ctx, void (*func)(void *));
    bool SwitchSelectedItem();
    void RefreshItemIQ3(void *ctx);
    void CreateItemsHandleV3(KeyboardValue key);
    virtual void Select(void *ctx);
    /**
     * @brief 
     * 
     * @return true 
     * @return false 
     */
    bool KeyCmdResetHandle();
    /**
     * @brief Create a Item View object
     * 派生类的创建一个设置项view控件抽象函数
     * @param create 创建的设置项功能序号，标识view序号
     * @param view2 创建好的有效的view控件，注意该指针应该是有效的
     * @return true 创建失败
     * @return false 创建成功
     */
    virtual bool CreateItemView(int create, Widget *view2) { return false; }
    /**
     * @brief Create a One Item object
     * 派生类的创建一个设置项的抽象函数，不同的派生类（一般是一个界面类）可能会有不同的实现方法。
     * @param view2 创建好的有效的view控件，注意该指针应该是有效的
     * @param iTarget 创建的设置项功能序号，用于查找设置项的基本数据（名称译文、数值等）
     */
    virtual void CreateOneItem(Widget *view2, int iTarget) {}
    /**
     * @brief Set the Item View object
     * 派生类的设置设置项view控件的抽象函数，一般是设置view控件的布局、样式
     * @param view 需要设置的目标view控件，注意该指针应该是有效的
     */
    virtual void SetItemView(Widget *view) {}
    /**
     * @brief Set the Selected Item object
     * 派生类的设置被选中并“确定”操作的设置项的抽象函数，一般是设置操作状态下设置项的属性
     * @param view 被选中并“确定”操作的设置项view控件
     */
    virtual void SetSelectedItem(Widget *view) {}
    /**
     * @brief 
     * 派生类的设置项被选中后不同界面的一些差异化操作，一般用于一些按键控件的显示/隐藏
     */
    virtual void ItemEvent() {}
    /**
     * @brief 
     * 设置项是否是有效，默认返回true表示忽略；一般用于虚拟功能的设置项（设置项总数随机）。
     * @param target 设置项序号
     * @return true 设置项存在
     * @return false 设置项不存在
     */
    virtual bool TargetItemValid(unsigned int target) { return true; }
    /**
     * @brief 
     * 设置项当前滚动条占比，用于虚拟功能的设置项，因为设置项总数随机，占比计算由派生类处理。
     * @param value 输出处理的占比
     * @return true 处理成功返回
     * @return false 处理失败返回，默认返回失败表示不存在虚拟功能
     */
    virtual bool VirtualSliderValue(int &value, unsigned int selected) { return false; }
    Widget *GetPrimaryPage()
    {
        return primaryWidget.get();
    }
    void RefreshItem(void *ctx)
    {
        IS_VALID_PTR(ctx)
        class Ctx : public ContextV2<KeyboardValue>
        {
        public:
            Ctx(std::string window, KeyboardValue key) : ContextV2(key), win(window) {}
            std::string win;
        };
        auto funCallback = [](void *ctx)
        {
            IS_VALID_PTR(ctx)
            Ctx *pFunc = (Ctx *)ctx;
            // std::lock_guard<std::mutex> locker(Interface::GetInstance().PageProtect());
            PAGE *window = (PAGE *)Interface::GetInstance().GetCurrentWin(pFunc->win.c_str());
            IS_VALID_PTR(window)
            window->RefreshItemIQ3(ctx);
            delete pFunc;
        };
        KeyboardValue key = KeyboardValue::KEYBOARD_VALUE_END;
        ((Context *)ctx)->GetData(key);
        Ctx *p = new Ctx(primaryWidget->Name(), key);
        p->eventCallback = funCallback;
        /**
         * @brief 
         * 取消队列方式，解决程序时序异常问题。
         */
        // if (!GuiEngine::GetInstance().IdleQueue(p))
        // {
        //     LOG_W("IdleQueue_V2 falied\n");
        //     delete p;
        // }
        RefreshItemIQ3(p);
    }
    /**
     * @brief 
     * 设置项的OK处理函数，一般是确定操作选定的设置项。
     * @tparam T 某个界面类
     * @param ctx 
     */
    void KeyOkEvent(void *ctx)
    {
        // IS_VALID_PTR(ctx)
        auto funCallback = [](void *ctx)
        {
            IS_VALID_PTR(ctx)
            ContextV2<std::string> *pFunc = (ContextV2<std::string> *)ctx;
            std::string data;
            pFunc->GetDataV2(data);
            // std::lock_guard<std::mutex> locker(Interface::GetInstance().PageProtect());
            PAGE *window = (PAGE *)Interface::GetInstance().GetCurrentWin(data.c_str());
            IS_VALID_PTR(window)
            /**
             * @brief 
             * 默认把所有按键重定向到设置项实例处理。
             * 暂未使用，框架设计，不要删除
             */
#if 0
            for (int i = 0; i < static_cast<int>(KeyboardValue::KEYBOARD_VALUE_END); i++)
            {
                window->KeyEventRedefine(static_cast<KeyboardValue>(i), KeyCmd::KEY_CMD_CONTRL_SECOND_PAGE);
            }
#endif
            /**
             * @brief 
             * 调用设置项绑定的功能函数，有可能包括恢复按键重定向到界面功能（一般指打开新的界面或者设置项只读等情况）
             */
            if (!window->KeyCmdResetHandle())
            {
                /**
                 * @brief 
                 * 如果设置项是只读的，或者初始化异常，恢复界面按键功能。
                 */
                window->UnlockSecondPage(nullptr);
            }
            delete pFunc;
        };
        std::string pageName = primaryWidget->Name();
        ContextV2<std::string> *p = new ContextV2<std::string>(pageName);
        p->eventCallback = funCallback;
        if (!GuiEngine::GetInstance().IdleQueue(p))
        {
            LOG_W("IdleQueue_V2 falied\n");
            delete p;
        }
    }
    /**
     * @brief 
     * 当前设置界面恢复按键事件，下一级窗口关闭时，可保持本界面按键功能正常。
     * @tparam T 界面的按键事件模板
     * @param ctx 
     */
    // template <typename T>
    void UnlockSecondPageV2(void *ctx)
    {
        LOG_I("unlock second page\n");
        KEYEVENT defaultEvent;
        PageHandle *window = (PageHandle *)Interface::GetInstance().GetCurrentWin();
        IS_VALID_PTR(window)
        window->SetKeyEvent(defaultEvent);
        if (SecondPageValid())
        {
            GetSecondPage().ContrlSecondPage(ctx);
        }
        SecondPageReset(nullptr);
    }
    /**
     * @brief 
     * 界面启动初始化，默认从第一个设置项开始。
     * @tparam T 界面类模板
     * @param PageName 界面名称
     */
    virtual void StartDisplay()
    {
        KeyboardValue key = KeyboardValue::KEY_DIRECTION_RIGHT;
        Context ctx;
        ctx.ctx = &key;
        funcSelected = static_cast<FUNC>(0);
        lastSelected = static_cast<FUNC>(static_cast<int>(FUNC::FUNCTION_END) - 1);
        RefreshItem(&ctx);
    }
    void RefreshSlider()
    {
        int value = 0;
        std::unique_ptr<Widget> view(GuiEngine::GetInstance().NewWidget());
        if (GuiEngine::GetInstance().Lookup(primaryWidget.get(), "slider", true, view.get()))
        {
            /**
             * @brief 
             * 虚拟功能是否存在，不使用虚拟功能时，忽略此判断;
             * 如果存在虚拟设置项，value的计算由派生类返回。
             */
            if (!VirtualSliderValue(value, static_cast<unsigned int>(funcSelected)))
            {
                /**
                 * @brief 
                 * 如果显示的所有item里面找到选择的下一个item，直接修改item属性进行选定
                 */
                int tmp = static_cast<int>(FUNC::FUNCTION_END);
                if(tmp - 1 == 0)
                {
                    return;
                }
                value = (100 / (tmp - 1)) * static_cast<int>(funcSelected);
            }
            value = 100 - value;
            /**
             * @brief 
             * 设置项总数可能无法被100整除，这里做简单的修正，放置选中最后一个设置项时，滚动条未滚动到底。
             */
            if (value <= 5)
            {
                value = 0;
            }
            GuiEngine::GetInstance().SliderSetValue(view.get(), value);
        }
    }
    /**
     * @brief Get the String object
     * 获取设置项的字符数据时，有三个GetString函数，一个是Item类虚函数，一个是Item子类重写函数，一个是当前文件的模板函数；
     * 调用顺序是模板函数->重写函数，有点绕（主要是实现抽象的处理流程）
     * @tparam T 
     * @param target 
     * @param itemPtr 
     * @return std::string 
     */
    std::string GetString(unsigned int target)
    {
        typename std::map<FUNC, Item *>::iterator iter;
        iter = itemPtr.find(static_cast<FUNC>(target));
        if (iter != itemPtr.end())
        {
            return itemPtr[static_cast<FUNC>(target)]->GetString();
        }
        else
        {
            LOG_E("something wrong happen\n");
            return "error";
        }
    }
    std::string GetStringTrText(unsigned int target)
    {
        typename std::map<FUNC, Item *>::iterator iter;
        iter = itemPtr.find(static_cast<FUNC>(target));
        if (iter != itemPtr.end())
        {
            return itemPtr[static_cast<FUNC>(target)]->GetStringTrText();
        }
        else
        {
            LOG_E("something wrong happen\n");
            return "error";
        }
    }
    template <typename T, typename T2>
    bool RenewValueHandle(Widget *page, T target, std::map<T, Item *> itemPtr)
    {
        char name2[16] = {0};
        sprintf(name2, "item%d", static_cast<int>(target));
        std::unique_ptr<Widget> view6(GuiEngine::GetInstance().NewWidget());
        if (GuiEngine::GetInstance().Lookup(page, name2, true, view6.get()))
        {
            GuiEngine::GetInstance().UseStyle(view6.get(), ITEM_FOCUS);
            typename std::map<T, Item *>::iterator iter;
            iter = itemPtr.find(target);
            if (iter != itemPtr.end())
            {
                ((T2 *)(itemPtr[target]))->SetContrlView(view6.get());
                ((T2 *)(itemPtr[target]))->RefreshPage();
                return true;
            }
            else
            {
                LOG_E("something wrong happen\n");
                return false;
            }
        }
        else
        {
            return false;
        }
    }
    std::string GetTrText(unsigned int target)
    {
        typename std::map<FUNC, Item *>::iterator iter;
        iter = itemPtr.find(static_cast<FUNC>(target));
        if (iter != itemPtr.end())
        {
            return itemPtr[static_cast<FUNC>(target)]->GetTrText();
        }
        else
        {
            LOG_E("something wrong happen\n");
            return "error";
        }
    }
    /**
     * @brief 
     * 打开新窗口的设置项，一般没有设置项的值，所以要把设置项名称设置为100%宽度
     * @param target 
     */
    void EnterItemHandle(unsigned int target)
    {
        typename std::map<FUNC, Item *>::iterator iter;
        iter = itemPtr.find(static_cast<FUNC>(target));
        if (iter != itemPtr.end())
        {
            if (!itemPtr[static_cast<FUNC>(target)]->GetEnterItem())
            {
                return;
            }
            std::unique_ptr<Widget> view6(GuiEngine::GetInstance().NewWidget());
            char name2[16] = {0};
            snprintf(name2, 16, "item%d", target);
            if (GuiEngine::GetInstance().Lookup(primaryWidget.get(), name2, true, view6.get()))
            {
                std::unique_ptr<Widget> view(GuiEngine::GetInstance().NewWidget());
                if (GuiEngine::GetInstance().Lookup(view6.get(), "setItem", true, view.get()))
                {
                    GuiEngine::GetInstance().SetSelfLayout(view.get(), "default(w=87%)");
                }
                if (GuiEngine::GetInstance().Lookup(view6.get(), "valueString", true, view.get()))
                {
                    GuiEngine::GetInstance().SetSelfLayout(view.get(), "default(w=1%)");
                }
            }
            /**
             * @brief 
             * 抽象流程，特殊设置项初始化
             */
            itemPtr[static_cast<FUNC>(target)]->EnterItemSpecialInit();
        }
    }
    void ItemSelectedHandle()
    {
        typename std::map<FUNC, Item *>::iterator iter;
        iter = itemPtr.find(funcSelected);
        if (iter != itemPtr.end())
        {
            SecondPage *p = dynamic_cast<SecondPage *>(itemPtr[funcSelected]);
            if (p)
            {
                p->ItemSelected();
            }
        }
        else
        {
            LOG_E("something wrong happen\n");
        }
    }
    /**
     * @brief 
     * 刷新所有数据。
     */
    void RefreshAllItem()
    {
        char name2[16] = {0};
        std::unique_ptr<Widget> view6(GuiEngine::GetInstance().NewWidget());
        for (int i = 0; i < static_cast<int>(FUNC::FUNCTION_END); i++)
        {
            name2[0] = 0;
            snprintf(name2, 16, "item%d", i);
            if (GuiEngine::GetInstance().Lookup(primaryWidget.get(), name2, true, view6.get()))
            {
                std::unique_ptr<Widget> view(GuiEngine::GetInstance().NewWidget());
                if (GuiEngine::GetInstance().Lookup(view6.get(), "valueString", true, view.get()))
                {
                    typename std::map<FUNC, Item *>::iterator iter;
                    iter = itemPtr.find(static_cast<FUNC>(i));
                    if (iter != itemPtr.end())
                    {
                        GuiEngine::GetInstance().SetTextUtf8(view.get(), itemPtr[static_cast<FUNC>(i)]->GetString().c_str());
                    }
                }
            }
        }
    }
    template <typename T>
    bool KeyCmdResetHandle(T funcSelected, std::map<T, std::function<void()>> reset, Widget *window)
    {
        typename std::map<T, std::function<void()>>::iterator iter;
        iter = reset.find(funcSelected);
        if (iter != reset.end())
        {
            reset[funcSelected]();
            char name2[16] = {0};
            snprintf(name2, 16, "item%d", static_cast<int>(funcSelected));
            std::unique_ptr<Widget> view6(GuiEngine::GetInstance().NewWidget());
            if (GuiEngine::GetInstance().Lookup(window, name2, true, view6.get()))
            {
                /**
                 * @brief 
                 * 把view控件设置为选中状态
                 */
                if (SecondPageValid())
                {
                    GetSecondPage().SetContrlView(view6.get());
                }
                GuiEngine::GetInstance().UseStyle(view6.get(), ITEM_SET);
                /**
                 * @brief 
                 * 把该item里面的两个label设置字体颜色
                 * 注意：有些item只有一个label，但代码不影响（有个error的打印请忽略）
                 */
                std::unique_ptr<Widget> view7(GuiEngine::GetInstance().NewWidget());
                if (GuiEngine::GetInstance().Lookup(view6.get(), VALUE_STRING_NAME, true, view7.get()))
                {
                    LOG_I("find value string view\n");
                    GuiEngine::GetInstance().UseStyle(view7.get(), ITEM_SET);
                }
                if (GuiEngine::GetInstance().Lookup(view6.get(), ITEM_LABEL_NAME, true, view7.get()))
                {
                    LOG_I("find value string view\n");
                    GuiEngine::GetInstance().UseStyle(view7.get(), ITEM_SET);
                }
            }
            return true;
        }
        else
        {
            /**
             * @brief 
             * 只读的参数没有reset功能映射，无需做任何处理
             */
            LOG_E("something wrong happen, ignore if setting item is read-only\n");
            return false;
        }
    }
    /**
     * @brief 
     * 通用的打开一个窗口的模板函数，一般用于按键功能的界面
     * @tparam PAGE2 界面模板类
     * @tparam KEYEVENT 按键事件模板
     * @tparam T 数据模板
     * @param win 窗口名称
     * @param page 带已读数据的窗口
     * @param data 数据，如果无数据，填0-T<int>
     */
    template <typename PAGE2, typename KEYEVENT2, typename T>
    static void OpenNewPage(std::string win, PAGE2 *page, T data)
    {
        class Ctx : public ContextV2<T>
        {
        public:
            Ctx(std::string window, PAGE2 *p, T data) : ContextV2<T>(data), win(window), page(p) {}
            std::string win;
            PAGE2 *page;
        };
        Ctx *p = new Ctx(win, page, data);
        auto funCallback = [](void *ctx) mutable
        {
            IS_VALID_PTR(ctx)
            Ctx *p2 = (Ctx *)ctx;
            KEYEVENT2 event;
            PAGE2 *window = nullptr;
            if (p2->page)
            {
                /**
                 * @brief 
                 * 带已读数据打开一个新窗口
                 */
                window = (PAGE2 *)p2->page;
            }
            else
            {
                window = new PAGE2;
            }
            if (!window->PageOpen(p2->win.c_str()))
            {
                LOG_E("window open error\n");
                delete window;
                delete p2;
                return;
            }
            window->SetKeyEvent(event);
            window->SaveContext(p2);
            // window->SecondHandle::StartDisplay();
            SecondHandle *p = dynamic_cast<SecondHandle*>(window);
            if(p)
            {
                p->StartDisplay();
            }
            // window->StartDisplay();
            window->KeyOkEvent(nullptr);
            Interface::GetInstance().SetCurrentPage(window);
        };
        p->eventCallback = funCallback;
        if (!GuiEngine::GetInstance().IdleQueue(p))
        {
            LOG_W("IdleQueue_V2 falied\n");
            delete p;
        }
    }
    template <typename PAGE2, typename KEYEVENT2, typename T>
    static void OpenNewPageV2(std::string win, PAGE2 *page, T data)
    {
        class Ctx : public ContextV2<T>
        {
        public:
            Ctx(std::string window, PAGE2 *p, T data) : ContextV2<T>(data), win(window), page(p) {}
            std::string win;
            PAGE2 *page;
        };
        Ctx *p = new Ctx(win, page, data);
        auto funCallback = [](void *ctx) mutable
        {
            IS_VALID_PTR(ctx)
            Ctx *p2 = (Ctx *)ctx;
            KEYEVENT2 event;
            PAGE2 *window = nullptr;
            /**
             * @brief 
             * 带已读数据打开一个新窗口
             */
            window = (PAGE2 *)p2->page;
            if (!window->PageOpen(p2->win.c_str()))
            {
                LOG_E("window open error\n");
                delete window;
                delete p2;
                return;
            }
            window->SetKeyEvent(event);
            window->SaveContext(p2);
            // window->SecondHandle::StartDisplay();
            SecondHandle *p = dynamic_cast<SecondHandle*>(window);
            if(p)
            {
                p->StartDisplay();
            }
            // window->StartDisplay();
            window->KeyOkEvent(nullptr);
            Interface::GetInstance().SetCurrentPage(window);
        };
        p->eventCallback = funCallback;
        if (!GuiEngine::GetInstance().IdleQueue(p))
        {
            LOG_W("IdleQueue_V2 falied\n");
            delete p;
        }
    }


public:
    /**
     * @brief 
     * 当前选中设置项的实例，当对设置项进行操作时使用，
     * 一般情况下，设置设置项时，new一个实例，保存到智能指针，然后对该实例进行操作。
     */
    std::unique_ptr<SecondPage> secondPage;
    std::mutex m_mutex;
    /**
     * @brief 
     * 主要的窗口，该类依赖一个窗口，控制主窗口的某个区域
     * 该指针只使用，不管理
     */
    // Widget **primaryPage;//弃用
    /**
     * @brief 
     * 主要的窗口，该类依赖一个窗口，控制主窗口的某个区域
     */
    std::unique_ptr<Widget> &primaryWidget;
    /**
     * @brief 
     * 当前功能选项
     */
    FUNC funcSelected;
    /**
     * @brief 
     * 上一个功能选择
     */
    FUNC lastSelected;
    /**
     * @brief 
     * 派生类设置页单页显示的最大设置项数量，超过这个数会进行翻页，翻页功能由本类实现。
     */
    const unsigned int ITEM_TOTAL;
    /**
     * @brief 
     * 设置项的实例，使用指针实现设置项的多态功能。
     */
    std::map<FUNC, Item *> itemPtr;
    /**
     * @brief 
     * 功能设置项的处理函数映射，一般用于设置项选定后，对设置项设置（数值改变、保存等）
     */
    std::map<FUNC, std::function<void()>> reset;
    /**
     * @brief 
     * 设置项选择是是否可以循环，例如最后一项的下一项是否可以循环切到第一项。
     * 默认为true
     */
    bool selectLoop;
};
#endif